import { Icon } from "@iconify-icon/react";
import { useForm } from "@tanstack/react-form";
import { useQuery } from "@tanstack/react-query";
import { openPath } from "@tauri-apps/plugin-opener";
import { arch } from "@tauri-apps/plugin-os";
import { useCallback, useEffect, useState } from "react";

import {
  commands as localSttCommands,
  events as localSttEvents,
  type SupportedSttModel,
} from "@hypr/plugin-local-stt";
import { type AIProvider, aiProviderSchema } from "@hypr/store";
import {
  Accordion,
  AccordionContent,
  AccordionItem,
  AccordionTrigger,
} from "@hypr/ui/components/ui/accordion";
import { Button } from "@hypr/ui/components/ui/button";
import { cn } from "@hypr/utils";

import { useBillingAccess } from "../../../../billing";
import { useListener } from "../../../../contexts/listener";
import { useIsMacos } from "../../../../hooks/usePlatform";
import * as main from "../../../../store/tinybase/main";
import { FormField, StyledStreamdown, useProvider } from "../shared";
import { ProviderId, PROVIDERS, sttModelQueries } from "./shared";

export function ConfigureProviders() {
  return (
    <div className="flex flex-col gap-3">
      <h3 className="text-sm font-semibold">Configure Providers</h3>
      <Accordion type="single" collapsible className="space-y-3">
        <HyprProviderCard
          providerId="hyprnote"
          providerName="Hyprnote"
          icon={
            <img src="/assets/icon.png" alt="Hyprnote" className="size-5" />
          }
          badge={PROVIDERS.find((p) => p.id === "hyprnote")?.badge}
        />
        {PROVIDERS.filter((provider) => provider.id !== "hyprnote").map(
          (provider) => (
            <NonHyprProviderCard key={provider.id} config={provider} />
          ),
        )}
      </Accordion>
    </div>
  );
}

function NonHyprProviderCard({
  config,
}: {
  config: (typeof PROVIDERS)[number];
}) {
  const billing = useBillingAccess();
  const [provider, setProvider] = useProvider(config.id);
  const locked = config.requiresPro && !billing.isPro;

  const form = useForm({
    onSubmit: ({ value }) => setProvider(value),
    defaultValues:
      provider ??
      ({
        type: "stt",
        base_url: config.baseUrl ?? "",
        api_key: "",
      } satisfies AIProvider),
    listeners: {
      onChange: ({ formApi }) => {
        queueMicrotask(() => {
          const {
            form: { errors },
          } = formApi.getAllErrors();
          if (errors.length > 0) {
            console.log(errors);
          }

          formApi.handleSubmit();
        });
      },
    },
    validators: { onChange: aiProviderSchema },
  });

  return (
    <AccordionItem
      disabled={config.disabled || locked}
      value={config.id}
      className="rounded-xl border-2 border-dashed bg-neutral-50"
    >
      <AccordionTrigger
        className={cn([
          "capitalize gap-2 px-4",
          (config.disabled || locked) && "cursor-not-allowed opacity-30",
        ])}
      >
        <div className="flex items-center gap-2">
          {config.icon}
          <span>{config.displayName}</span>
          {config.badge && (
            <span className="text-xs text-neutral-500 font-light border border-neutral-300 rounded-full px-2">
              {config.badge}
            </span>
          )}
        </div>
      </AccordionTrigger>
      <AccordionContent className="px-4">
        <ProviderContext providerId={config.id} />

        <form
          className="space-y-4"
          onSubmit={(e) => {
            e.preventDefault();
            e.stopPropagation();
          }}
        >
          {!config.baseUrl && (
            <form.Field name="base_url">
              {(field) => (
                <FormField field={field} label="Base URL" icon="mdi:web" />
              )}
            </form.Field>
          )}
          <form.Field name="api_key">
            {(field) => (
              <FormField
                field={field}
                label="API Key"
                icon="mdi:key"
                placeholder="Enter your API key"
                type="password"
              />
            )}
          </form.Field>
          {config.baseUrl && (
            <details className="space-y-4 pt-2">
              <summary className="text-xs cursor-pointer text-neutral-600 hover:text-neutral-900 hover:underline">
                Advanced
              </summary>
              <div className="mt-4">
                <form.Field name="base_url">
                  {(field) => (
                    <FormField field={field} label="Base URL" icon="mdi:web" />
                  )}
                </form.Field>
              </div>
            </details>
          )}
        </form>
      </AccordionContent>
    </AccordionItem>
  );
}

function HyprProviderCard({
  providerId,
  providerName,
  icon,
  badge,
}: {
  providerId: ProviderId;
  providerName: string;
  icon: React.ReactNode;
  badge?: string | null;
}) {
  const isMacos = useIsMacos();
  const targetArch = useQuery({
    queryKey: ["target-arch"],
    queryFn: () => arch(),
    staleTime: Infinity,
  });
  const isAppleSilicon = isMacos && targetArch.data === "aarch64";

  return (
    <AccordionItem
      value={providerId}
      className="rounded-xl border-2 border-dashed bg-neutral-50"
    >
      <AccordionTrigger className={cn(["capitalize gap-2 px-4"])}>
        <div className="flex items-center gap-2">
          {icon}
          <span>{providerName}</span>
          {badge && (
            <span className="text-xs text-neutral-500 font-light border border-neutral-300 rounded-full px-2">
              {badge}
            </span>
          )}
        </div>
      </AccordionTrigger>
      <AccordionContent className="px-4">
        <ProviderContext providerId={providerId} />
        <div className="space-y-3">
          <HyprProviderCloudRow />

          <div className="flex items-center gap-3 py-2">
            <div className="flex-1 border-t border-dashed border-neutral-300" />
            <a
              href="https://hyprnote.com/docs/developers/local-models"
              target="_blank"
              rel="noopener noreferrer"
              className="text-xs text-neutral-400 hover:underline flex items-center gap-1"
            >
              <span>or use on-device model</span>
              <Icon icon="mdi:help-circle-outline" className="size-3" />
            </a>
            <div className="flex-1 border-t border-dashed border-neutral-300" />
          </div>

          {isAppleSilicon && (
            <>
              <HyprProviderLocalRow
                model="am-parakeet-v2"
                displayName="Parakeet v2"
                description="English only. Works best for English."
              />
              <HyprProviderLocalRow
                model="am-parakeet-v3"
                displayName="Parakeet v3"
                description="English and European languages."
              />
              <HyprProviderLocalRow
                model="am-whisper-large-v3"
                displayName="Whisper Large v3"
                description="Broad coverage of languages."
              />

              <details className="space-y-4 pt-2">
                <summary className="text-xs cursor-pointer text-neutral-600 hover:text-neutral-900 hover:underline">
                  Advanced
                </summary>
                <div className="mt-4 space-y-3">
                  <HyprProviderLocalRow
                    model="QuantizedTinyEn"
                    displayName="whisper-tiny-en-q8"
                    description="Only for experiment & development purposes."
                  />
                  <HyprProviderLocalRow
                    model="QuantizedSmallEn"
                    displayName="whisper-small-en-q8"
                    description="Only for experiment & development purposes."
                  />
                </div>
              </details>
            </>
          )}

          {!isAppleSilicon && (
            <>
              <HyprProviderLocalRow
                model="QuantizedTinyEn"
                displayName="whisper-tiny-en-q8"
                description="Powered by Whisper.cpp. English only."
              />
              <HyprProviderLocalRow
                model="QuantizedSmallEn"
                displayName="whisper-small-en-q8"
                description="Powered by Whisper.cpp. English only."
              />
            </>
          )}
        </div>
      </AccordionContent>
    </AccordionItem>
  );
}

function HyprProviderRow({ children }: { children: React.ReactNode }) {
  return (
    <div
      className={cn([
        "flex items-center justify-between",
        "py-2 px-3 rounded-md border bg-white",
      ])}
    >
      {children}
    </div>
  );
}

function HyprProviderCloudRow() {
  const { isPro, upgradeToPro } = useBillingAccess();

  const handleSelectProvider = main.UI.useSetValueCallback(
    "current_stt_provider",
    (provider: string) => provider,
    [],
    main.STORE_ID,
  );

  const handleSelectModel = main.UI.useSetValueCallback(
    "current_stt_model",
    (model: string) => model,
    [],
    main.STORE_ID,
  );

  const handleClick = useCallback(() => {
    if (!isPro) {
      upgradeToPro();
    } else {
      handleSelectProvider("hyprnote");
      handleSelectModel("cloud");
    }
  }, [isPro, upgradeToPro, handleSelectProvider, handleSelectModel]);

  return (
    <HyprProviderRow>
      <div className="flex items-center justify-between w-full">
        <div className="flex flex-col gap-1">
          <span className="text-sm font-medium">Hyprnote Cloud (Beta)</span>
          <span className="text-xs text-neutral-500">
            Use the Hyprnote Cloud API to transcribe your audio.
          </span>
        </div>
        <Button
          onClick={handleClick}
          className="w-[110px]"
          size="sm"
          variant="default"
          disabled={!isPro}
        >
          {isPro ? "Ready to use" : "Pro users only"}
        </Button>
      </div>
    </HyprProviderRow>
  );
}

function LocalModelAction({
  isDownloaded,
  showProgress,
  progress,
  hasError,
  onOpen,
  onDownload,
  onCancel,
}: {
  isDownloaded: boolean;
  showProgress: boolean;
  progress: number;
  hasError: boolean;
  onOpen: () => void;
  onDownload: () => void;
  onCancel: () => void;
}) {
  return (
    <Button
      size="sm"
      className={cn([
        "w-[110px] relative overflow-hidden group",
        hasError && "border-red-500",
      ])}
      variant={isDownloaded ? "outline" : hasError ? "destructive" : "default"}
      onClick={isDownloaded ? onOpen : showProgress ? onCancel : onDownload}
    >
      {showProgress && (
        <div
          className="absolute inset-0 bg-black/50 transition-all duration-300"
          style={{ width: `${progress}%` }}
        />
      )}
      {isDownloaded ? (
        <div className="relative z-10 flex items-center gap-1">
          <Icon icon="mdi:folder-open" size={16} />
          <span>Show Model</span>
        </div>
      ) : hasError ? (
        <div className="relative z-10 flex items-center gap-2">
          <Icon icon="mdi:alert-circle" size={16} />
          <span>Retry</span>
        </div>
      ) : showProgress ? (
        <>
          <div className="relative z-10 flex items-center gap-2 group-hover:hidden">
            <Icon icon="mdi:loading" size={16} className="animate-spin" />
            <span>{Math.round(progress)}%</span>
          </div>
          <div className="relative z-10 hidden items-center gap-2 group-hover:flex">
            <Icon icon="mdi:close" size={16} />
            <span>Cancel</span>
          </div>
        </>
      ) : (
        <div className="relative z-10 flex items-center gap-2">
          <Icon icon="mdi:download" size={16} />
          <span>Download</span>
        </div>
      )}
    </Button>
  );
}

function HyprProviderLocalRow({
  model,
  displayName,
  description,
}: {
  model: SupportedSttModel;
  displayName: string;
  description: string;
}) {
  const handleSelectModel = useSafeSelectModel();

  const {
    progress,
    hasError,
    isDownloaded,
    showProgress,
    handleDownload,
    handleCancel,
  } = useLocalModelDownload(model, handleSelectModel);

  const handleOpen = () =>
    localSttCommands.modelsDir().then((result) => {
      if (result.status === "ok") {
        openPath(result.data);
      }
    });

  return (
    <HyprProviderRow>
      <div className="flex flex-col gap-1">
        <span className="text-sm font-medium">{displayName}</span>
        <span className="text-xs text-neutral-500">{description}</span>
      </div>

      <LocalModelAction
        isDownloaded={isDownloaded}
        showProgress={showProgress}
        progress={progress}
        hasError={hasError}
        onOpen={handleOpen}
        onDownload={handleDownload}
        onCancel={handleCancel}
      />
    </HyprProviderRow>
  );
}

function useLocalModelDownload(
  model: SupportedSttModel,
  onDownloadComplete?: (model: SupportedSttModel) => void,
) {
  const [progress, setProgress] = useState<number>(0);
  const [isStarting, setIsStarting] = useState(false);
  const [hasError, setHasError] = useState(false);

  const isDownloaded = useQuery(sttModelQueries.isDownloaded(model));
  const isDownloading = useQuery(sttModelQueries.isDownloading(model));

  const showProgress =
    !isDownloaded.data && (isStarting || (isDownloading.data ?? false));

  useEffect(() => {
    if (isDownloading.data) {
      setIsStarting(false);
    }
  }, [isDownloading.data]);

  useEffect(() => {
    const unlisten = localSttEvents.downloadProgressPayload.listen((event) => {
      if (event.payload.model === model) {
        if (event.payload.progress < 0) {
          setHasError(true);
          setIsStarting(false);
          setProgress(0);
        } else {
          setHasError(false);
          const next = Math.max(0, Math.min(100, event.payload.progress));
          setProgress(next);
        }
      }
    });

    return () => {
      unlisten.then((fn) => fn());
    };
  }, [model]);

  useEffect(() => {
    if (isDownloaded.data && progress > 0) {
      setProgress(0);
      onDownloadComplete?.(model);
    }
  }, [isDownloaded.data, model, onDownloadComplete, progress]);

  const handleDownload = () => {
    if (isDownloaded.data || isDownloading.data || isStarting) {
      return;
    }
    setHasError(false);
    setIsStarting(true);
    setProgress(0);
    localSttCommands.downloadModel(model).then((result) => {
      if (result.status === "error") {
        setHasError(true);
        setIsStarting(false);
      }
    });
  };

  const handleCancel = () => {
    localSttCommands.cancelDownload(model);
    setIsStarting(false);
    setProgress(0);
  };

  return {
    progress,
    hasError,
    isDownloaded: isDownloaded.data ?? false,
    showProgress,
    handleDownload,
    handleCancel,
  };
}

function ProviderContext({ providerId }: { providerId: ProviderId }) {
  const content =
    providerId === "hyprnote"
      ? "Hyprnote curates list of on-device models and also cloud models with high-availability and performance."
      : providerId === "deepgram"
        ? `Use [Deepgram](https://deepgram.com) for transcriptions. \
    If you want to use a [Dedicated](https://developers.deepgram.com/reference/custom-endpoints#deepgram-dedicated-endpoints)
    or [EU](https://developers.deepgram.com/reference/custom-endpoints#eu-endpoints) endpoint,
    you can do that in the **advanced** section.`
        : providerId === "soniox"
          ? `Use [Soniox](https://soniox.com) for transcriptions.`
          : providerId === "assemblyai"
            ? `Use [AssemblyAI](https://www.assemblyai.com) for transcriptions.`
            : providerId === "gladia"
              ? `Use [Gladia](https://www.gladia.io) for transcriptions.`
              : providerId === "openai"
                ? `Use [OpenAI](https://openai.com) for transcriptions.`
                : providerId === "fireworks"
                  ? `Use [Fireworks AI](https://fireworks.ai) for transcriptions.`
                  : providerId === "custom"
                    ? `We only support **Deepgram compatible** endpoints for now.`
                    : "";

  if (!content.trim()) {
    return null;
  }

  return <StyledStreamdown className="mb-6">{content.trim()}</StyledStreamdown>;
}

function useSafeSelectModel() {
  const handleSelectModel = main.UI.useSetValueCallback(
    "current_stt_model",
    (model: SupportedSttModel) => model,
    [],
    main.STORE_ID,
  );

  const active = useListener((state) => state.live.status !== "inactive");

  const handler = useCallback(
    (model: SupportedSttModel) => {
      if (active) {
        return;
      }
      handleSelectModel(model);
    },
    [active, handleSelectModel],
  );

  return handler;
}
